package com.tsd.common.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tsd.common.entity.CommonFile;
import com.tsd.common.entity.CommonFileExt;
import com.tsd.common.service.CommonFileService;
import com.tsd.core.config.Config;
import com.tsd.core.constants.ConfigKeyConstants;
import com.tsd.core.constants.Constants;
import com.tsd.core.datasource.DataSourceContextHolder;
import com.tsd.core.handler.NonStaticResourceHttpRequestHandler;
import com.tsd.core.storage.Storage;
import com.tsd.core.utils.*;
import com.tsd.core.vo.ErrorCode;
import com.tsd.core.vo.HlpException;
import com.tsd.core.vo.HttpResult;
import com.tsd.core.vo.PageBean;
import com.tsd.storage.StorageService;
import com.tsd.system.entity.SysUser;
import com.tsd.system.entity.SysUserExt;
import com.tsd.core.utils.HlpUtils;
import com.tsd.utils.DownloadUtil;
import io.jsonwebtoken.Claims;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import springfox.documentation.annotations.ApiIgnore;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * 描述：CommonFile文件表 控制层
 * 文件上传分为4种模式：
 * 1）直接使用form上传  uploadFile.action
 * 2）切片Form上传  uploadFormFileSlice.action
 * 3）转为base64上传  uploadFileBase64.action
 * 4）切片base64上传 uploadFileBase64Slice.action
 *
 * @author huawei
 * @date 2021/08/08 20:40:38
 */
@Controller
@Scope("prototype")
@RequestMapping("/file")
@Api(tags = "通用-文件表", description = "文件表的API描述")
public class CommonFileController extends BaseController {
    private static final Logger LOGGER = LoggerFactory.getLogger("quartzLogger");

    @Resource
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
    @Resource
    NonStaticResourceHttpRequestHandler nonStaticResourceHttpRequestHandler;

    @Resource
    private CommonFileService commonFileService;
    @Resource
    private StorageService storageService;


    @ApiOperation(value = "获取OSS配置")
    @ResponseBody
    @RequestMapping(value = "/getOssConfig.action", method = RequestMethod.POST)
    public HttpResult<Object> getOssConfig() {
        HttpResult<Object> hr = new HttpResult<>();
        try {
            JSONObject object = new JSONObject();
            object.put("local", StorageService.STORE_TYPE_LOCAL);
            object.put("active", storageService.getActive());
            if (!StorageService.STORE_TYPE_LOCAL.equals(storageService.getActive())) {
                Storage storage = storageService.getStorage();
                object.put("base_url", storage.generateUrl(""));
            }
            hr.setData(object);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    @ApiOperation(value = "根据sid获取文件表", notes = "根据sid获取文件表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sid", value = "文件表sid", required = true, dataType = "string", paramType = "form")
    })
    @ResponseBody
    @RequestMapping(value = "/getCommonFileBySid.action", method = RequestMethod.POST)
    public HttpResult<CommonFileExt> getCommonFileBySid() {
        HttpResult<CommonFileExt> hr = new HttpResult<>();
        try {
            Map<String, Object> map = super.getParams(request);
            String sid = HlpUtils.getString(map, "sid");
            super.checkEmpty(sid, "请提供参数sid");
            super.setAccountSetFromParams();
            CommonFileExt commonFile = commonFileService.queryBySid(sid);
            hr.setData(commonFile);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    @ApiOperation(value = "根据sid列表删除文件表", notes = "根据sid删除文件表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "sids", value = "文件表sids", required = true, dataType = "string", paramType = "form")
    })
    @ResponseBody
    @RequestMapping(value = "/deleteCommonFiles.action", method = RequestMethod.POST)
    public HttpResult<String> deleteCommonFiles() {
        HttpResult<String> hr = new HttpResult<>();
        try {
            SysUser opUser = super.getSysUser();
            Map<String, Object> map = super.getParams(request);
            super.setAccountSetFromParams();
            Object sids = map.get("sids");
            super.checkEmpty(sids, "请提供参数sids");
            List<String> list = JSON.parseArray(JSON.toJSONString(sids), String.class);
            commonFileService.deleteCommonFiles(list, opUser);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }


    @ApiOperation(value = "保存文件表", notes = "保存文件表，具有id值则更新，没有id值则新增")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "obj", value = "文件表", required = true, paramType = "body", dataType = "CommonFile")
    })
    @ResponseBody
    @RequestMapping(value = "/saveCommonFile.action", method = RequestMethod.POST)
    public HttpResult<String> saveCommonFile() {
        HttpResult<String> hr = new HttpResult<>();
        try {
            SysUser opUser = super.getSysUser();
            Map<String, Object> map = super.getParams(request);
            super.setAccountSetFromParams();
            String json = JSON.toJSONString(map);
            CommonFile t = JSON.parseObject(json, CommonFile.class);
            commonFileService.saveCommonFile(t, opUser);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    @ApiOperation(value = "分页查询文件表", notes = "分页查询文件表")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "name", value = "名称", required = false, dataType = "string", paramType = "form"),
            @ApiImplicitParam(name = "pageNo", value = "页码", required = false, dataType = "int", paramType = "form"),
            @ApiImplicitParam(name = "pageSize", value = "页大小，每页记录数", required = false, dataType = "int", paramType = "form"),
            @ApiImplicitParam(name = "sorts", value = "排序设置，方式['property1 desc','property2']", required = false, dataType = "String", paramType = "form"),
            @ApiImplicitParam(name = "totalRows", value = "总记录数，为了减少统计总记录数，一般从第二页开始前端提供totalRows", required = false, dataType = "int", paramType = "form")
    })
    @ResponseBody
    @RequestMapping(value = "/findCommonFiles.action", method = RequestMethod.POST)
    public HttpResult<List<CommonFileExt>> findCommonFiles() {
        HttpResult<List<CommonFileExt>> hr = new HttpResult<List<CommonFileExt>>();
        try {
            Map<String, Object> map = super.getParams(request);
            super.setAccountSetFromParams();
            Map<String, Object> params = new HashMap<>();
            HlpUtils.copyIntegerValue(map, params, "dr", "dr", false, 0);
            HlpUtils.copyStringValue(map, params, "biz_type", "biz_type", false, false);
            HlpUtils.copyStringValue(map, params, "associate_biz_sid", "associate_biz_sid", false, false);
            HlpUtils.copyStringValue(map, params, "associate_biz_table", "associate_biz_table", false, false);
            HlpUtils.copyStringValue(map, params, "search_value", "search_value", false, true);
            PageBean page = new PageBean(map);
            List<CommonFileExt> list = commonFileService.findCommonFiles(params, page);
            hr.setData(list);
            hr.setPage(page);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }


    @ApiOperation(value = "下载文件", notes = "根据文件sid下载")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "sid", value = "文件sid", required = true)})
    @RequestMapping(value = "/downloadFile")
    public void downloadFile(HttpServletResponse response) throws Exception {
        Map<String, Object> map = super.getParams(request);
        String sid = HlpUtils.getString(map, "sid");
        super.checkEmpty(sid, "请提供参数sid");
        super.setAccountSetFromParams();
        DownloadUtil.procDownloadFile(commonFileService, sid, request, response);
    }

    @ApiOperation(value = "下载文件", notes = "根据文件id下载")
    @RequestMapping(value = "/file/{accountSetSid}/{sid}")
    public void file(@PathVariable("sid") String sid, @PathVariable("accountSetSid") String accountSetSid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        DataSourceContextHolder.setDataSourceType(accountSetSid);
        DownloadUtil.procDownloadFile(commonFileService, sid, request, response);
    }

    @ApiOperation(value = "下载Excel模板文件")
    @RequestMapping(value = "/downloadExcelTemplate/{accountSetSid}/{sid}")
    public void downloadExcelTemplate(@PathVariable("sid") String sid, @PathVariable("accountSetSid") String accountSetSid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        DataSourceContextHolder.setDataSourceType(accountSetSid);
        DownloadUtil.procDownloadExcelTemplateFile(sid, request, response);
    }

    @ApiOperation(value = "下载文件", notes = "根据文件id下载")
    @RequestMapping(value = "/file/{sid}")
    public void file(@PathVariable("sid") String sid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        super.setAccountSetFromParams();
        DownloadUtil.procDownloadFile(commonFileService, sid, request, response);
    }

    @ApiOperation(value = "下载文件", notes = "根据文件id下载")
    @RequestMapping(value = "/fileWithType/{accountSetSid}/{sid}")
    public void fileWithType(@PathVariable("sid") String sid, @PathVariable("accountSetSid") String accountSetSid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        int index = sid.lastIndexOf(".");
        if (index > 0) {
            sid = sid.substring(0, index);
        }
        DataSourceContextHolder.setDataSourceType(accountSetSid);
        DownloadUtil.procDownloadFile(commonFileService, sid, request, response);
    }

    @ApiOperation(value = "下载文件", notes = "根据文件id下载")
    @RequestMapping(value = "/fileWithType/{sid}")
    public void fileWithType(@PathVariable("sid") String sid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        int index = sid.lastIndexOf(".");
        if (index > 0) {
            sid = sid.substring(0, index);
        }
        super.setAccountSetFromParams();
        DownloadUtil.procDownloadFile(commonFileService, sid, request, response);
    }

    @ApiOperation(value = "下载视频文件", notes = "根据文件sid下载视频文件")
    @RequestMapping(value = "/video/{accountSetSid}/{sid}")
    public void video(@PathVariable("sid") String sid, @PathVariable("accountSetSid") String accountSetSid, HttpServletResponse response) throws Exception {
        super.checkEmpty(sid, "请提供参数sid");
        DataSourceContextHolder.setDataSourceType(accountSetSid);
        DownloadUtil.downloadStatic(commonFileService, sid, nonStaticResourceHttpRequestHandler, request, response);
    }

    @ApiOperation(value = "根据sid获取fileInfo对象", notes = "根据文件sid下载")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "sid", value = "文件id", required = true)})
    @ResponseBody
    @RequestMapping(value = "/getFileInfo.action", method = RequestMethod.POST)
    public HttpResult<CommonFileExt> getFileInfo() {
        HttpResult<CommonFileExt> hr = new HttpResult<>();
        try {
            Map<String, Object> map = super.getParams(request);
            String sid = HlpUtils.getString(map, "sid");
            super.checkEmpty(sid, "请提供参数sid");
            super.setAccountSetFromParams();
            CommonFileExt fileInfo = commonFileService.getFileInfoBySid(sid);
            fileInfo.setFile_type(FileUtil.getFileType(fileInfo.getOriginal_file_name()));
            hr.setData(fileInfo);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    @ApiOperation(value = "根据idList获取fileInfo对象类别", notes = "根据文件id下载")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "id", value = "文件id", required = true)})
    @ResponseBody
    @RequestMapping(value = "/getFileList.action", method = RequestMethod.POST)
    public HttpResult<Object> getFileList() {
        HttpResult<Object> hr = new HttpResult<>();
        try {
            super.setAccountSetFromParams();
            List<String> sidList = super.getParamsList("sids", String.class, true);
            List<CommonFileExt> fileList = commonFileService.getCommonFilesBySidList(sidList);
            for (CommonFileExt info : fileList) {
                info.setFile_type(FileUtil.getFileType(info.getOriginal_file_name()));
            }
            hr.setData(fileList);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    @ApiOperation(value = "删除未使用文件", notes = "已经在实际业务中使用的文件不能删除")
    @ApiImplicitParams({
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "ids", value = "逗号分隔的id", required = true)})
    @ResponseBody
    @RequestMapping(value = "/deleteFileInfo4Unused.action", method = RequestMethod.POST)
    public HttpResult<String> deleteFileInfo4Unused(@ApiIgnore @RequestAttribute Claims claims) {
        HttpResult<String> hr = new HttpResult<String>();
        try {
            Map<String, Object> map = super.getParams(request);
            SysUser sysUser = super.getSysUser();
            String ids = (String) map.get("ids");
            super.checkEmpty(ids, "请提供参数ids");
            super.setAccountSetFromParams();
            commonFileService.deleteFileInfo4Notused(ids, sysUser);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    /**
     * 描述：分页查询FileInfo
     */
    @ResponseBody
    @RequestMapping(value = "/findFileInfos4Unused.action", method = RequestMethod.POST)
    public HttpResult<List<CommonFileExt>> findFileInfos4Unused() {
        HttpResult<List<CommonFileExt>> hr = new HttpResult<>();
        try {
            Map<String, Object> map = super.getParams(request);
            Map<String, Object> params = new HashMap<String, Object>();
            SysUser opUser = super.getSysUser();
            super.checkEmpty(opUser, "请先登录");
            params.put("owner_sid", opUser.getSid());
            params.put("state", CommonFileExt.StateNotUsed);
            PageBean page = new PageBean(map);
            super.setAccountSetFromParams();
            List<CommonFileExt> list = commonFileService.findCommonFiles(params, page);
            hr.setData(list);
            hr.setPage(page);
            hr.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(hr, e);
        }
        return hr;
    }

    /**
     * form请求参数包括： file--》上传文件，
     * 参数以url内的方式提交，所以不要注意中文的使用
     * biz_type:'',//业务类型,作为业务路径的一部分
     * associate_biz_table:'',//业务数据库表名
     * associate_biz_sid:'',//业务数据库记录sid
     * associate_biz_field_tag:'',//业务数据字段标识，目的是为了区分一个表多种用途的附件文件
     * <p>
     * 返回CommonFile对象，该对象存储了文件的路径路径以及文件访问的web url、原文件名称
     * 建议业务数据表不需要存放CommonFile对象的sid，而是通过业务表的字段查询CommonFile
     * 当然业务表创建文件sid字段，进行双向绑定也是可以的。
     * 一般用法：
     * 1）业务数据编辑时，上传文件得到CommonFile对象列表，在保存业务数据时，需要将所有的有效文件sid作为json数据字符串提交，在service层调调用如下方法：
     * //更新相关附件信息
     * commonFileService.updateAssociateData(record.getFile_ids(),"sale_enquiry",record.getSid(),"attach",null,null);
     * 2）文件的删除，采用物理删除的方式，调用后台后直接生效，如果业务数据存放文件sid列表，则需要调用业务数据保存接口，否则会出现文件已删除而业务数据的文件列表还存在的问题
     * 3）未使用的文件可以作为临时文件进行定时清除
     * 4）当业务数据被物理删除时，需要调用文件物理删除的方法进行清除记录和物理文件
     * Element UI的例子
     * vue的赋值：
     * token: this.$store.state.accessToken.token,
     * account_set_sid:this.$store.state.accountSetSid,
     * <p>
     * <el-upload
     * class="uploadPicture"
     * :class="{ head: isHead }"
     * :action="fileApiUrl.uploadFile"
     * :headers="{ token: token}"
     * :data="{associate_biz_table:'sys_user',associate_biz_sid:userForm.sid,associate_biz_field_tag:'head_pic',biz_type:'head_pic',account_set_sid:account_set_sid }"
     * list-type="picture-card"
     * :on-success="handleHeadImgSuccess"
     * :on-preview="handleHeadImgCardPreview"
     * :file-list="userForm.headPicPreview"
     * :on-remove="handleHeadImgRemove"
     * accept="image/*"
     * style="float: left">
     * <i>上传头像</i>
     *     <div slot="tip" class="el-upload__tip">1:1正方形，jpg或png格式图片</div>
     * </el-upload>
     *
     * @param file 返回CommonFile对象，该对象存储了文件的路径路径以及文件访问的web url、原文件名称
     * @return
     */
    @ApiOperation(value = "上传文件（管理端）")
    @ApiImplicitParams({@ApiImplicitParam(paramType = "form", dataType = "string", name = "token", value = "token", required = true),
            @ApiImplicitParam(paramType = "form", dataType = "file", name = "file", value = "文件流", required = true),
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "bizPath", value = "业务路径", required = true)})
    @ResponseBody
    @RequestMapping(value = "/uploadFile.action", method = RequestMethod.POST)
    public HttpResult<Object> uploadFile(@RequestParam(value = "file") MultipartFile file, HttpServletRequest request) {
        HttpResult<Object> resp = new HttpResult<Object>();
        try {
            SysUserExt opUser = super.getSysUserFromToken(request);
            super.checkEmpty(opUser, "请先登录");

            String associate_biz_table = request.getParameter("associate_biz_table");
            String associate_biz_sid = request.getParameter("associate_biz_sid");
            String associate_biz_field_tag = request.getParameter("associate_biz_field_tag");
            String biz_type = request.getParameter("biz_type");
            String account_set_sid = HttpUtil.getAccountSet(request);
            DataSourceContextHolder.setDataSourceType(account_set_sid);
            /**
             * 业务路径默认是业务类型，可以为空，如果指定，则在rootPath下建立对应目录，然后将文件复制到相应目录
             */
            String biz_path = biz_type;
            if (HlpUtils.isEmpty(biz_path)) {
                biz_path = "default";
            }
            File targetFile = new File(getFilePath2Save4AccountSet(account_set_sid, biz_path, file.getOriginalFilename()));
            file.transferTo(targetFile);
            CommonFile fi = addFileInfo(opUser, biz_type, targetFile.getAbsolutePath(), file.getOriginalFilename(), associate_biz_table, associate_biz_sid,
                    associate_biz_field_tag, null, null, null);
            resp.setData(fi);
            resp.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(resp, e);
        }
        return resp;
    }

    /**
     * 包含账套信息的文件保存路径
     *
     * @param account_set_sid
     * @param biz_path
     * @param originalFilename
     * @return
     */
    private String getFilePath2Save4AccountSet(String account_set_sid, String biz_path, String originalFilename) {
        return getFilePath2Save(HlpUtils.isEmpty(account_set_sid) ? biz_path : account_set_sid + "/" + biz_path, originalFilename);
    }

    /**
     * form请求参数包括： file--》上传文件，
     * 参数以url内的方式提交，所以不要注意中文的使用
     * biz_type:'',//业务类型,作为业务路径的一部分
     * associate_biz_table:'',//业务数据库表名
     * associate_biz_sid:'',//业务数据库记录sid
     * associate_biz_field_tag:'',//业务数据字段标识，目的是为了区分一个表多种用途的附件文件
     * <p>
     * 返回CommonFile对象，该对象存储了文件的路径路径以及文件访问的web url、原文件名称
     * 建议业务数据表不需要存放CommonFile对象的sid，而是通过业务表的字段查询CommonFile
     * 当然业务表创建文件sid字段，进行双向绑定也是可以的。
     * 一般用法：
     * 1）业务数据编辑时，上传文件得到CommonFile对象列表，在保存业务数据时，需要将所有的有效文件sid作为json数据字符串提交，在service层调调用如下方法：
     * //更新相关附件信息
     * commonFileService.updateAssociateData(record.getFile_ids(),"sale_enquiry",record.getSid(),"attach",null,null);
     * 2）文件的删除，采用物理删除的方式，调用后台后直接生效，如果业务数据存放文件sid列表，则需要调用业务数据保存接口，否则会出现文件已删除而业务数据的文件列表还存在的问题
     * 3）未使用的文件可以作为临时文件进行定时清除
     * 4）当业务数据被物理删除时，需要调用文件物理删除的方法进行清除记录和物理文件
     *
     * @param file 返回CommonFile对象，该对象存储了文件的路径路径以及文件访问的web url、原文件名称
     * @return
     */
    @ApiOperation(value = "Form切片上传文件（管理端）")
    @ApiImplicitParams({@ApiImplicitParam(paramType = "form", dataType = "string", name = "token", value = "token", required = true),
            @ApiImplicitParam(paramType = "form", dataType = "file", name = "file", value = "文件流", required = true),
            @ApiImplicitParam(paramType = "form", dataType = "String", name = "bizPath", value = "业务路径", required = true)})
    @ResponseBody
    @RequestMapping(value = "/uploadFormFileSlice.action", method = RequestMethod.POST)
    public HttpResult<Object> uploadFormFileSlice(@RequestParam(value = "file") MultipartFile file, HttpServletRequest request) {
        HttpResult<Object> resp = new HttpResult<Object>();
        try {
            SysUserExt opUser = super.getSysUserFromToken(request);
            super.checkEmpty(opUser, "请先登录");
            // 上传的根目录
            String rootPath = Config.getParameterValue(ConfigKeyConstants.UPLOAD_ROOT_PATH);
            String fileInfoId = request.getParameter("fileInfoId");
            String associate_biz_table = request.getParameter("associate_biz_table");
            String associate_biz_sid = request.getParameter("associate_biz_sid");
            String associate_biz_field_tag = request.getParameter("associate_biz_field_tag");
            String biz_type = request.getParameter("biz_type");
            String sliceIndexStr = request.getParameter("sliceIndex");
            String sliceCountStr = request.getParameter("sliceCount");
            Integer sliceCount = Integer.parseInt(sliceCountStr);
            Integer sliceIndex = Integer.parseInt(sliceIndexStr);
            String sliceSizeStr = request.getParameter("sliceSize");
            Integer sliceSize = Integer.parseInt(sliceSizeStr);
            String fileName = request.getParameter("fileName");
            /**
             * 业务路径，可以为空，如果指定，则在rootPath下建立对应目录，然后将文件复制到相应目录
             */
            String biz_path = biz_type;
            if (HlpUtils.isEmpty(biz_path)) {
                biz_path = "default";
            }
            String ext = FileUtil.getFileType(fileName);
            String lngStr = request.getParameter("lng");
            String latStr = request.getParameter("lat");
            Double lng = lngStr == null ? null : Double.parseDouble(lngStr);
            Double lat = latStr == null ? null : Double.parseDouble(latStr);
            String totalLengthStr = request.getParameter("totalLength");
            Long totalLength = totalLengthStr == null ? 0L : Long.parseLong(totalLengthStr);
            byte[] bytes = file.getBytes();
            String account_set_sid = HttpUtil.getAccountSet(request);
            DataSourceContextHolder.setDataSourceType(account_set_sid);

            CommonFile fi;
            if (HlpUtils.isEmpty(fileInfoId)) {
                File targetFile = new File(getFilePath2Save4AccountSet(account_set_sid, biz_path, fileName));
                fi = addFileInfo(opUser, biz_type, targetFile.getAbsolutePath(), fileName, associate_biz_table, associate_biz_sid, associate_biz_field_tag, lat, lng, totalLength);
            } else {
                fi = commonFileService.getFileInfoBySid(fileInfoId);
            }
            long start = (long) sliceIndex * sliceSize;
            FileUtil.appendFile(fi.getFile_path(), start, bytes);
            long fileLength = (long) sliceIndex * sliceSize + bytes.length;
            if (fileLength - totalLength >= 0) {
                //当totalLength=(sliceIndex+1)*sliceSize+当前长度时，表示上传完毕
                fi.setState(-999);
                videoFileTrans(fi);
            } else {
                fi.setState(sliceIndex + 1);
            }
            resp.setData(fi);
            resp.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(resp, e);
        }
        return resp;
    }

    /**
     * 视频文件转换
     *
     * @param fi
     */
    private void videoFileTrans(CommonFile fi) {
        String filePath = fi.getFile_path().toLowerCase();
        if (filePath.endsWith(".quicktime") || filePath.endsWith(".mov")) {
            LOGGER.info("IOS 视频文件转换到MP4：" + filePath);
//					threadPoolTaskExecutor.execute(() -> {});
            try {
                FfMpegUtil ffMpegUtil = new FfMpegUtil();
                ffMpegUtil.convertFile2mp4(filePath);
                CommonFile update = new CommonFile();
                update.setId(fi.getId());
                update.setFile_path(filePath + ".mp4");
                update.setOriginal_file_name(fi.getOriginal_file_name() + ".mp4");
                commonFileService.saveCommonFile(update, null);
                fi.setFile_path(update.getFile_path());
                fi.setOriginal_file_name(fi.getOriginal_file_name() + ".mp4");
            } catch (Exception e) {
                LOGGER.error(ExceptionUtil.getStackTraceFromException(e));
            }
        }
    }

    @ApiOperation(value = "上传文件（Base64）")
    @ResponseBody
    @RequestMapping(value = "/uploadFileBase64.action", method = RequestMethod.POST)
    public HttpResult<Object> uploadFileBase64() {
        HttpResult<Object> resp = new HttpResult<Object>();
        try {
            Map<String, Object> map = super.getParams(request);
            SysUserExt opUser = super.getSysUserFromToken(request);
            String base64 = HlpUtils.getString(map, "base64");
            super.checkEmpty(base64, "缺少参数：base64");
            // 上传的根目录
            String biz_type = HlpUtils.getString(map, "biz_type");
            String associate_biz_table = HlpUtils.getString(map, "associate_biz_table");
            String associate_biz_sid = HlpUtils.getString(map, "associate_biz_sid");
            String associate_biz_field_tag = HlpUtils.getString(map, "associate_biz_field_tag");
            String account_set_sid = HttpUtil.getAccountSet(request);
            DataSourceContextHolder.setDataSourceType(account_set_sid);
            String fileName = HlpUtils.getString(map, "fileName");
            String ext = FileUtil.getBase64FileType(base64);
            if (HlpUtils.isEmpty(fileName)) {
                fileName = "upload." + ext;
            }
            /**
             * 业务路径，可以为空，如果指定，则在rootPath下建立对应目录，然后将文件复制到相应目录
             */
            String bizPath = biz_type;
            if (HlpUtils.isEmpty(bizPath)) {
                bizPath = "default";
            }
            File targetFile = new File(getFilePath2Save4AccountSet(account_set_sid, bizPath, fileName));
            String filePath = targetFile.getAbsolutePath();
            FileUtil.saveFile(filePath + ".txt", base64.getBytes(StandardCharsets.UTF_8), true);
            FileUtil.base64ToFile(base64, filePath);

            CommonFile fi = addFileInfo(opUser, biz_type, filePath, targetFile.getName(), associate_biz_table, associate_biz_sid, associate_biz_field_tag, null, null, null);
            resp.setData(fi);

            resp.setCode(ErrorCode.CODE_SUCCESS);
        } catch (Exception e) {
            super.procException(resp, e);
        }
        return resp;
    }

    @ApiOperation(value = "切片上传文件（Base64）")
    @ResponseBody
    @RequestMapping(value = "/uploadFileBase64Slice.action", method = RequestMethod.POST)
    public HttpResult<Object> uploadFileBase64Slice() {
        HttpResult<Object> resp = new HttpResult<Object>();
        try {
            Map<String, Object> map = super.getParams(request);
            SysUserExt opUser = super.getSysUserFromToken(request);
            String slice = HlpUtils.getString(map, "slice");
            super.checkEmpty(slice, "缺少参数：slice");
            Integer sliceIndex = HlpUtils.getInteger(map, "sliceIndex", 0);//片断序号，从0开始
            super.checkEmpty(sliceIndex, "缺少参数：sliceIndex");
            Integer sliceSize = HlpUtils.getInteger(map, "sliceSize");//片断长度
            super.checkEmpty(sliceSize, "缺少参数：sliceSize");
            if (sliceSize % 4 != 0) {
                throw new HlpException("片段长度（sliceSize）必须是4的倍数");
            }
            Long totalLength = HlpUtils.getLong(map, "totalLength");//总长度
            super.checkEmpty(totalLength, "缺少参数：totalLength");
            // 上传的根目录
            String biz_type = HlpUtils.getString(map, "biz_type");
            String fileInfoId = HlpUtils.getString(map, "fileInfoId");
            String associate_biz_table = HlpUtils.getString(map, "associate_biz_table");
            String associate_biz_sid = HlpUtils.getString(map, "associate_biz_sid");
            String associate_biz_field_tag = HlpUtils.getString(map, "associate_biz_field_tag");
            String ext = HlpUtils.getString(map, "fileType");
            String account_set_sid = HttpUtil.getAccountSet(request);
            DataSourceContextHolder.setDataSourceType(account_set_sid);
            String fileName = HlpUtils.getString(map, "fileName");
            if (HlpUtils.isEmpty(fileName)) {
                fileName = "upload." + ext;
            }
            /**
             * 业务路径，可以为空，如果指定，则在rootPath下建立对应目录，然后将文件复制到相应目录
             */
            String bizPath = biz_type;
            if (HlpUtils.isEmpty(bizPath)) {
                bizPath = "default";
            }
            CommonFileExt fi;
            if (HlpUtils.isEmpty(fileInfoId)) {
                File targetFile = new File(getFilePath2Save4AccountSet(account_set_sid, bizPath, fileName));
                String filePath = targetFile.getAbsolutePath();
                Double lng = HlpUtils.getDouble(map, "lng");
                Double lat = HlpUtils.getDouble(map, "lat");
                fi = addFileInfo(opUser, biz_type, filePath, targetFile.getName(), associate_biz_table, associate_biz_sid, associate_biz_field_tag, lat, lng, totalLength);
            } else {
                fi = commonFileService.getFileInfoBySid(fileInfoId);
            }
            long start = (long) sliceIndex * sliceSize * 3 / 4;
            Base64.Decoder decoder = Base64.getDecoder();
            byte[] bytes = decoder.decode(slice);
            FileUtil.appendFile(fi.getFile_path(), start, bytes);
            long fileLength = (long) sliceIndex * sliceSize + slice.length();
            if (fileLength - totalLength >= 0) {
                //当totalLength=(sliceIndex+1)*sliceSize+当前长度时，表示上传完毕
                fi.setState(-999);
                videoFileTrans(fi);
                fi.setFile_type(FileUtil.getFileType(fi.getOriginal_file_name()));
                commonFileService.saveCommonFile(fi, null);
            } else {
                fi.setState(sliceIndex + 1);
            }
            resp.setCode(ErrorCode.CODE_SUCCESS);
            resp.setData(fi);
        } catch (Exception e) {
            super.procException(resp, e);
        }
        return resp;
    }

    /**
     * 根据源名称得到需要保存的目标文件路径吗,会保留原名，如果存在重名的，则则后缀之前加上1，2，...的序号
     *
     * @param bizPath          页面路径，账套id+业务类型
     * @param originalFileName 源文件名
     * @return
     */
    public String getFilePath2Save(String bizPath, String originalFileName) {
        // 上传的根目录
        String rootPath = Config.getParameterValue(ConfigKeyConstants.UPLOAD_ROOT_PATH);
        // 上传文件在web访问时的ulr前缀
        String urlPrefix = Config.getParameterValue(ConfigKeyConstants.UPLOAD_FILE_URL_PREFIX);
        Calendar cal = Calendar.getInstance();
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);
//        System.out.println(year + " " + month + " " + day);
        String midPath = File.separator + bizPath + File.separator + year + File.separator + month + File.separator + day;
        File rootAndMidPathFile = new File(rootPath + midPath);
        if (!rootAndMidPathFile.exists()) {
            rootAndMidPathFile.mkdirs();
        }
        urlPrefix = HlpUtils.isEmpty(urlPrefix) ? midPath : urlPrefix + midPath;
        String extWithDot = FileUtil.getFileType(originalFileName);
        String name = FileUtil.getFileNameWithoutExt(originalFileName);
        String destFileName = originalFileName;
        int index = 1;
        while (FileUtil.isFileExist(rootAndMidPathFile.getAbsolutePath() + File.separator + destFileName)) {
            destFileName = name + "_" + index + extWithDot;
            index++;
        }

        File targetFile = new File(rootAndMidPathFile, destFileName);
        return targetFile.getAbsolutePath();
    }

    public CommonFileExt addFileInfo(SysUserExt opUser, String bizTypeId, String absoluteFilePath, String originalFileName, String bizTable, String bizSid, String fieldTag, Double lat, Double lng, Long srcLength) throws Exception {
        CommonFileExt fi = new CommonFileExt();
        if (CommonFileExt.EXCEL_TEMPLATE_BIZ_TYPE.equals(bizTypeId)) {
            CommonFileExt existFile = commonFileService.queryExcelTemplateByBizSid(bizSid);
            if (existFile != null) {
                fi.setId(existFile.getId());
                fi.setSid(existFile.getSid());
            }
        }
        fi.setOwner_sid(opUser.getSid());
        fi.setBiz_type(bizTypeId);
        fi.setFile_path(absoluteFilePath);
        fi.setOriginal_file_name(originalFileName);
        fi.setAssociate_biz_field_tag(fieldTag);
        fi.setAssociate_biz_sid(bizSid);
        fi.setAssociate_biz_table(bizTable);
        fi.setState(HlpUtils.isEmpty(bizSid) ? CommonFileExt.StateNotUsed : CommonFileExt.StateUsed);
        fi.setLat(lat);
        fi.setLng(lng);
        if (srcLength != null) {
            fi.setFile_len(srcLength);
        }
        commonFileService.saveCommonFile(fi, opUser);
        return fi;
    }

    /**
     * @param sid           文件唯一编码 uuid
     * @param accountSetSid 账套sid
     * @return void
     * @describe 通过nginx下载文件
     * Nginx的配置：
     * # 文件下载服务
     * location /file_server/ {
     * # 内部请求（即一次请求的Nginx内部请求），禁止外部访问，重要。
     * internal;
     * # 文件路径
     * alias D:/uploads/
     * #limit_rate 200k;
     * # 浏览器访问返回200，然后转由后台处理
     * error_page 404 =200 @backend;
     * }
     * # 文件下载鉴权
     * location @backend {
     * # 去掉访问路径中的 /file_server/，然后定义新的请求地址。
     * rewrite ^/file_server/(.*)$ /tsdapi/file/downloadByNginx/$1 break;
     * # 这里的url后面不可以再拼接地址
     * proxy_pass http://127.0.0.1:8080;
     * proxy_redirect   off;
     * proxy_set_header Host $host;
     * proxy_set_header X-Real-IP $remote_addr;
     * proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
     * }
     * @author wudw
     * @date 2022-7-01 13:44
     */
    @GetMapping("/downloadByNginx/{accountSetSid}/{sid}")
    public void downloadByNginx(@PathVariable("sid") String sid, @PathVariable("accountSetSid") String accountSetSid, HttpServletResponse response) {
        DataSourceContextHolder.setDataSourceType(accountSetSid);
        // 通过唯一编码查询附件
        CommonFile fileInfo = null;
        try {
            fileInfo = commonFileService.getFileInfoBySid(sid);
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (fileInfo == null) {
            this.errorPage(response, 404);
            return;
        }
        File appFile = new File(fileInfo.getFile_path());
        if (!appFile.exists()) {
            this.errorPage(response, 404);
            return;
        }
        String fileName = null;
        try {
            fileName = new String(fileInfo.getOriginal_file_name().getBytes(StandardCharsets.UTF_8), StandardCharsets.ISO_8859_1);
        } catch (Exception e) {
            fileName = fileInfo.getOriginal_file_name();
        }
        String rootPath = Config.getParameterValue(ConfigKeyConstants.UPLOAD_ROOT_PATH);
        String fileSubPath = appFile.getAbsolutePath();
        if (!HlpUtils.isEmpty(rootPath)) {
            fileSubPath = fileSubPath.substring(rootPath.length());
        }
        fileSubPath = fileSubPath.replace("\\", "/");
        try {
            fileSubPath = URLEncoder.encode(fileSubPath, "utf-8");
        } catch (Exception e) {
            e.printStackTrace();
        }
        // 已被授权访问
        // 文件下载
        response.setHeader("Content-Disposition", "attachment; filename=\"" + fileName + "\"");
        // 文件以二进制流传输
        response.setHeader("Content-Type", "application/octet-stream;charset=utf-8");
        // 返回真实文件路径交由 Nginx 处理，保证前端无法看到真实的文件路径。
        // 这里的 "/file_server" 为 Nginx 中配置的下载服务名
        response.setHeader("X-Accel-Redirect", "/file_server/" + fileSubPath);
        // 限速，单位字节，默认不限
        // response.setHeader("X-Accel-Limit-Rate","1024");
        // 是否使用Nginx缓存，默认yes
        // response.setHeader("X-Accel-Buffering","yes");
        response.setHeader("X-Accel-Charset", "utf-8");

        // 禁止浏览器缓存
        response.setHeader("Pragma", "No-cache");
        response.setHeader("Cache-Control", "No-cache");
        response.setHeader("Expires", "0");
    }

    private void errorPage(HttpServletResponse response, int code) {
        response.setStatus(code);
    }
}
